#!/usr/bin/env python2

import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk

import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.figure import Figure
from matplotlib.backends.backend_gtk3agg import FigureCanvasGTK3Agg as FigureCanvas
from matplotlib.backends.backend_gtk3 import NavigationToolbar2GTK3 as NavigationToolbar

class DCM_CCM_Window(Gtk.Window):
	def __init__(self):
		Gtk.Window.__init__(self)
		self.set_title("Boost Converter DCM CCM Interface")
		self.set_default_size(1200, 900)
		self.set_resizable(False)
		self.connect("destroy", Gtk.main_quit)

		mainpane = Gtk.Paned(orientation=Gtk.Orientation.HORIZONTAL)
		mainpane.set_position(360)
		mainpane.set_halign(Gtk.Align.FILL)
		mainpane.set_valign(Gtk.Align.FILL)
		self.add(mainpane)
		
		configbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
		mainpane.add(configbox)

		displaybox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
		mainpane.add(displaybox)

		self._createScales(configbox)
		self._initializePlot(displaybox)

		self.dutyCycleScale.connect("change-value", self.updatePlot)
		self.outputVoltageScale.connect("change-value", self.updatePlot)
		self.loadResScale.connect("change-value", self.updatePlot)
		self.inductanceScale.connect("change-value", self.updatePlot)
		self.freqScale.connect("change-value", self.updatePlot)

	def _createScales(self, configbox):

		dutyCycleLabel = Gtk.Label("Duty Cycle [%]")
		configbox.add(dutyCycleLabel)

		dcBounds = [1, 99]
		dcDefault = 50

		self.dutyCycleScale = Gtk.Scale(orientation=Gtk.Orientation.HORIZONTAL, adjustment=None)
		self.dutyCycleScale.set_range(dcBounds[0], dcBounds[1])
		self.dutyCycleScale.set_value(dcDefault)
		self.dutyCycleScale.set_value_pos(Gtk.PositionType.BOTTOM)
		configbox.add(self.dutyCycleScale)

		outputVoltageLabel = Gtk.Label("Output Voltage [V]")
		configbox.add(outputVoltageLabel)

		ovBounds = [1, 50]
		ovDefault = 25

		self.outputVoltageScale = Gtk.Scale(orientation=Gtk.Orientation.HORIZONTAL, adjustment=None)
		self.outputVoltageScale.set_range(ovBounds[0], ovBounds[1])
		self.outputVoltageScale.set_value(ovDefault)
		self.outputVoltageScale.set_value_pos(Gtk.PositionType.BOTTOM)
		configbox.add(self.outputVoltageScale)

		loadResLabel = Gtk.Label("Load Resistance [R]")
		configbox.add(loadResLabel)

		lrBounds = [1, 50]
		lrDefault = 25

		self.loadResScale = Gtk.Scale(orientation=Gtk.Orientation.HORIZONTAL, adjustment=None)
		self.loadResScale.set_range(lrBounds[0], lrBounds[1])
		self.loadResScale.set_value(lrDefault)
		self.loadResScale.set_value_pos(Gtk.PositionType.BOTTOM)
		configbox.add(self.loadResScale)

		inductanceLabel = Gtk.Label("Inductance [nH]")
		configbox.add(inductanceLabel)
	
		indBounds = [1, 10000]
		indDefault = 2500
		self.inductanceScale = Gtk.Scale(orientation=Gtk.Orientation.HORIZONTAL, adjustment=None)
		self.inductanceScale.set_range(indBounds[0], indBounds[1])
		self.inductanceScale.set_value(indDefault)
		self.inductanceScale.set_value_pos(Gtk.PositionType.BOTTOM)
		configbox.add(self.inductanceScale)

		freqLabel = Gtk.Label("Frequency [kHz]")
		configbox.add(freqLabel)

		freqBounds = [20, 100]
		freqDefault = 50
		self.freqScale = Gtk.Scale(orientation=Gtk.Orientation.HORIZONTAL, adjustment=None)
		self.freqScale.set_range(freqBounds[0], freqBounds[1])
		self.freqScale.set_value(freqDefault)
		self.freqScale.set_value_pos(Gtk.PositionType.BOTTOM)
		configbox.add(self.freqScale)
	

	def _initializePlot(self, displaybox):

		self.fig = Figure()
		self.ax = self.fig.add_subplot(111)
		canvas = FigureCanvas(self.fig)
		displaybox.pack_start(canvas, True, True, 0)

		self.dutyVector = np.linspace(0, 1, 1000)
		self.invDutyVector = 1 - self.dutyVector

		dutyCycle = self.dutyCycleScale.get_value() 
		Vout = self.outputVoltageScale.get_value()
		Rload = self.loadResScale.get_value() 
		L = self.inductanceScale.get_value()*1e-9 
		freq = self.freqScale.get_value()*1e3

		# Derived Values
		Iout = Vout / Rload
		Vin = Vout*(1 - dutyCycle/100.0);

		Iin = (Vout*Iout) / Vin;
		I_LB = (Vout*self.invDutyVector*self.dutyVector) / (L*freq)
		I_OB = (Vout*(self.invDutyVector**2)*self.dutyVector) / (2*L*freq)

		# I_LB
		self.line_LB, = self.ax.plot(100*self.dutyVector, I_LB, 'b')

		# I_OB
		self.line_OB, = self.ax.plot(100*self.dutyVector, I_OB, 'r')

		# I_IN
		self.line_IN, = self.ax.plot(dutyCycle, Iin, 'go')

		# I_OUT
		self.line_OUT, = self.ax.plot(dutyCycle, Iout, 'ko')

		self.ax.set_xlabel('Duty Cycle [%]')
		self.ax.set_ylabel('Current [A]')
		self.ax.set_title('DCM CCM Mode Boost Converter')

	def updatePlot(self, rangeObj, scrollObj, value):

		dutyCycle = self.dutyCycleScale.get_value() 
		Vout = self.outputVoltageScale.get_value()
		Rload = self.loadResScale.get_value() 
		L = self.inductanceScale.get_value()*1e-9 
		freq = self.freqScale.get_value()*1e3

		# Derived Values
		Iout = Vout / Rload
		Vin = Vout*(1 - dutyCycle/100.0);

		Iin = (Vout*Iout) / Vin;
		I_LB = (Vout*self.invDutyVector*self.dutyVector) / (L*freq)
		I_OB = (Vout*(self.invDutyVector**2)*self.dutyVector) / (2*L*freq)

		# "Delete" the background contents -> Repaints an empty background
		self.ax.draw_artist(self.ax.patch)

		# Set New Data
		# I_LB
		self.line_LB.set_data(100.0*self.dutyVector, I_LB)
		self.ax.draw_artist(self.line_LB)

		# I_OB
		self.line_OB.set_data(100.0*self.dutyVector, I_OB)
		self.ax.draw_artist(self.line_OB)

		# I_IN
		self.line_IN.set_data(dutyCycle, Iin)
		self.ax.draw_artist(self.line_IN)

		# I_OUT
		self.line_OUT.set_data(dutyCycle, Iout)
		self.ax.draw_artist(self.line_OUT)
		
		currentData = []
		currentData.extend(I_LB)
		currentData.extend(I_OB)
		currentData.append(Iout)
		currentData.append(Iin)
		maxCurrent = np.max(currentData)
		currentLimit = maxCurrent + 0.10*maxCurrent
		self.ax.set_ylim([0, currentLimit])

		# Update figure
		self.fig.canvas.draw()
		self.fig.canvas.flush_events()


if __name__ == '__main__':
	window = DCM_CCM_Window()
	window.connect("destroy", Gtk.main_quit)
	window.show_all()
	Gtk.main()

